package com.fhc.usb

import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.hardware.usb.UsbDevice
import android.hardware.usb.UsbManager
import com.fhc.usb.Cmd.Companion.PREAMBLE
import com.hoho.android.usbserial.driver.UsbSerialDriver
import com.hoho.android.usbserial.driver.UsbSerialPort
import com.hoho.android.usbserial.driver.UsbSerialProber
import com.hoho.android.usbserial.util.SerialInputOutputManager
import timber.log.Timber
import java.util.concurrent.Executor
import java.util.concurrent.Executors
import java.util.concurrent.LinkedBlockingQueue

data class UsbSerialParams(
    var baudRate: Int = 115200, //比如 115200
    var dataBits: Int = 8, //比如 8
    var stopBits: Int = UsbSerialPort.STOPBITS_1, //比如 UsbSerialPort.STOPBITS_1
    var parity: Int = UsbSerialPort.PARITY_NONE, //比如 UsbSerialPort.PARITY_NONE
    var debug: Boolean = true
)

class UsbSerialManager private constructor(
    private val applicationContext: Context,
    private val params: UsbSerialParams,
    private val receiver: (String) -> Unit
) : SerialInputOutputManager.Listener {
    // 伴生对象，持有唯一实例
    companion object {
        @Volatile
        private var instance: UsbSerialManager? = null

        // 获取唯一实例的方法，线程安全
        fun getInstance(
            context: Context,
            params: UsbSerialParams,
            receiver: (String) -> Unit
        ): UsbSerialManager {
            return instance ?: synchronized(this) {
                instance ?: UsbSerialManager(context, params, receiver).also {
                    instance = it
                }
            }
        }
        fun instance() = instance
    }

    private var port: UsbSerialPort? = null
    private lateinit var usbIoManager: SerialInputOutputManager
    private val dataQueue = LinkedBlockingQueue<Byte>()
    private var cmd: Cmd = Cmd()
    private val singleExecutor: Executor by lazy {
        Executors.newSingleThreadExecutor()
    }
    private val readRunnable: Runnable
    private val usbManager = applicationContext.getSystemService(Context.USB_SERVICE) as UsbManager

    private var usbDevice: UsbDevice? = null
    init {
        readRunnable = Runnable {
            while (port?.isOpen == true || dataQueue.isNotEmpty()) {
                val byte = dataQueue.take()
                if (byte == PREAMBLE) { //前导帧
                    cmd = Cmd()
                    cmd.preamble = PREAMBLE
                    cmd.length = dataQueue.take()
                Timber.i("前导帧:0x${HexUtil.toByteString(PREAMBLE)}  长度:${cmd.length}")
                    readPayload(dataQueue)
                    val checkSum = dataQueue.take()
                    cmd.checkSum = checkSum
                Timber.i("校验:0x${HexUtil.toByteString(checkSum)}")
                    receiver.invoke(String(cmd.payload.toByteArray()))
                Timber.i("${String(cmd.payload.toByteArray())}      payload:${HexUtil.bytesToHexString(cmd.payload.toByteArray())}     )")
                    cmd.clear()
                } else {
//                    Timber.i("被抛弃:0x%s", HexUtil.toByteString(byte))
                }
            }
        }
    }

    private fun readPayload(dataStack: LinkedBlockingQueue<Byte>) {
        for (i in 0 until cmd.length) {
            cmd.payload.add(dataStack.take())
        }
    }

    fun requestPermission() {
        val driver = getDriver() ?: return
        val flags = PendingIntent.FLAG_IMMUTABLE
        val permissionIntent = PendingIntent.getBroadcast(
            applicationContext,
            0,
            Intent("com.android.example.USB_PERMISSION"),
            flags
        )
        usbManager.requestPermission(driver.device, permissionIntent)
    }

    private fun getDrivers(): MutableList<UsbSerialDriver> {
        return UsbSerialProber.getDefaultProber().findAllDrivers(usbManager)
    }

    private fun getDriver(): UsbSerialDriver? {
        val availableDrivers = getDrivers()
        if (availableDrivers.isEmpty()) {
            log("availableDrivers is empty.")
            return null
        }

        return availableDrivers[0]
    }

    fun hasPermission(): Boolean {
        val driver = getDriver() ?: return false
        return usbManager.hasPermission(driver.device)
    }

    fun getActiveDevice(): UsbDevice? {
        return  usbDevice
    }
    fun openDevice(onDeviceGet: (UsbDeviceDto) -> Unit ={}) {
        if (port?.isOpen == true) {
            Timber.i("port is opened")
            return
        }
        val driver = getDriver() ?: return
        debugLogDrivers(onDeviceGet)
        usbDevice = driver.device
        val connection = usbManager.openDevice(driver.device) ?: return
        Timber.i("connection:$connection")

        port = driver.ports[0] // Most devices have just one port (port 0)
        port?.open(connection)
        port?.setParameters(params.baudRate, params.dataBits, params.stopBits, params.parity)

        usbIoManager = SerialInputOutputManager(port, this)
        usbIoManager.start()
        singleExecutor.execute(readRunnable)

        Timber.i("usbIoManager.start")
    }

    private fun debugLogDrivers(onDeviceGet: (UsbDeviceDto) -> Unit ={}) {
        //  deviceId:1002  deviceName:/dev/bus/usb/001/002  deviceProtocol:0
        //  productName:CP2102N USB to UART Bridge Controller
        //  productId:60000 manufacturerName:Silicon Labs configurationCount:1 serialNumber:46ab11271bb4eb11863f92d8bbbaf228 vendorId:4292
        if (params.debug) {
            getDrivers().forEach {
                val device = it.device
                onDeviceGet(UsbDeviceDto.from(device)!!)
                Timber.i(
                    "deviceId:${device.deviceId}   deviceName:${device.deviceName}   deviceProtocol:${device.deviceProtocol}   productName:${device.productName}  productId:${device.productId}  manufacturerName:${device.manufacturerName}  configurationCount:${device.configurationCount}  serialNumber:${device.serialNumber}  vendorId:${device.vendorId}"
                )
            }
        }
    }

    fun write(data:String){
        if (port?.isOpen == true) {
            Timber.e(">>>>>>>>   UsbSerialManager.instance()?.write   $data   <<<<<<<<<<<<<")
            port?.write((data+"\r\n").toByteArray(), 1000)
        }
    }

    fun closeDevice() {
        usbDevice = null
        port?.close()
        port = null
    }

    private fun receive(data: ByteArray?) {
        data?.let {
            receiver.invoke(String(data))
        }
//        for (byte in data) {
//            dataQueue.put(byte)
//        }
    }

    override fun onNewData(data: ByteArray?) {
        receive(data)
    }

    override fun onRunError(e: Exception?) {
        Timber.i("onRunError:${e?.message}")
    }

    private fun log(message: String, tag: String = "Heiko") {
        Timber.i(message)
    }
}

class Cmd {
    companion object {
        const val PREAMBLE: Byte = 0xAA.toByte()
    }

    var preamble: Byte? = null
    var length: Byte = -1
    var payload = ArrayList<Byte>()
    var checkSum: Byte? = null

    fun clear() {
        preamble = null
        length = -1
        payload.clear()
        checkSum = null
    }
}

